Amazon IVSのプライベートチャンネル用tokenを手元のPython環境で作成してみた
はじめに
清水です。先日のアップデートでプライベートチャンネル機能が追加されたAmazon Interactive Video Service(Amazon IVS)、以下の公式ブログやワークショップ資料などで具体的な手順が公開されていますね。
- Introducing Private Channels for Amazon Interactive Video Service | AWS Media Blog
- 再生承認の有効化 :: Amazon Interactive Video Service Workshop
動作の概要としては動画プレイヤー(Amazon IVS Player)からPlayback URLへのアクセス時にクエリ文字列でtokenを付与する必要がある、というところまでは把握できたのですが、別途AWS LambdaやAmazon API Gatewayまわりを準備する部分が個人的にしっくりきていませんでした。LambdaやAPI Gatewayを使わずに、手元の環境(ふだん使っているmacOSやLinuxサーバ上)でこのJSON Web Token(JWT)を生成できないものかと思い、やってみたのでまとめてみます。具体的にはPythonの対話型インタプリタ上で必要な情報を入力しながらJWTを生成しました。
実際にこの手元での環境、つまりLambdaなどではない環境でもJWTの生成は可能で、これを使ったAmazon IVSのプライベートチャンネルの再生ももちろん可能でした。ただ実際にサービスに組み込む場合などには、何らかの認証機能などと組み合わせてJWTを生成することになり、その際にLambda+API Gatewayな環境を想定しているのかなぁと推測しています。
IVSのPlayback keyとプライベートチャンネルの準備
まずはIVSでPlayback keyならびにプライベートチャンネルを準備します。
Playback keyの作成
Playback keyをAWSマネジメントコンソールから作成します。「Playback keys」のページにて[Create playback key]ボタンを押下し、ダイアログでキー名称を入力します。
[Create]ボタンでkeyを作成します。これでキーペアが作成され、公開鍵についてはAWSでリソースとして管理されます。秘密鍵についてはブラウザからダウンロードできるので、保存しておきます。なおダウンロードできるのはこのタイミングのみですので注意しましょう。ダウンロードした秘密鍵はivs-playback-key-1.pem
という名称で保存しておきました。
プライベートチャンネルの作成
続いてIVSでプライベートチャンネルを作成します。マネジメントコンソールのIVSのページ、[Create Channel]ボタンから進みSetupのChannel configurationでCustom configuration
を選択、Playback authorizationの項目でEnable toke-authorization requirement for video playback
を有効にします。
チャンネル作成後、Playback authorizationの項目が有効になっていること、またLive streamのプレビュー部分が利用できないこと(プライベートチャンネルのため)を確認しておきます。
Pythonの対話型インタプリタ環境でのJWTの生成
続いてPython環境からJWTを生成していきます。
Python環境の準備
今回環境としてはAmazon Linux 2でEC2インスタンスを起動し、Python3環境をyumでインストールしていきました。
[ec2-user@ip-10-82-21-161 ~]$ sudo yum install python3 -y
以下のように、python3
コマンドならびにpip3
コマンドが使用できる環境となりました。
[ec2-user@ip-10-82-21-161 ~]$ which python3 /usr/bin/python3 [ec2-user@ip-10-82-21-161 ~]$ python3 --version Python 3.7.9 [ec2-user@ip-10-82-21-161 ~]$ which pip3 /usr/bin/pip3 [ec2-user@ip-10-82-21-161 ~]$ pip3 --version pip 9.0.3 from /usr/lib/python3.7/site-packages (python 3.7)
続いてPython環境でJWTを扱うライブラリとしてpyjwt
、そしてES384で署名の際に使用するcryptography
をpipでインストールしておきます。Python環境でJWTを扱うライブラリとしては他にもpython-jose
などいくつかあるかと思いますが、今回は公式ドキュメントからも参照されているページjwt.ioにてPythonでFilterした際に一番最初に出てきたことからpyjwt
を選択しています。また、アルゴリズムとしてES384
を指定して署名しようとしたところ、「NotImplementedError: Algorithm 'ES384' could not be found. Do you have cryptography installed?」というエラーが現れ、こちらのページを参考にcryptography
もインストールした、という流れです。なおrootでのpip installは推奨されていません。(その旨、警告も現れます。)実際の環境に適したかたちで実行ください。
[ec2-user@ip-10-82-21-161 ~]$ sudo pip3 install pyjwt [ec2-user@ip-10-82-21-161 ~]$ sudo pip3 install cryptography
pip list
すると以下の状態です。
[ec2-user@ip-10-82-21-161 ~]$ pip3 list Package Version ------------ ------- cffi 1.14.3 cryptography 3.1.1 pip 9.0.3 pycparser 2.20 PyJWT 1.7.1 setuptools 38.4.0 six 1.15.0
JWT生成時の情報の確認
続いて、JWTを生成する際の各情報をあらかじめ確認しておきます。IVS公式ドキュメントの以下ページを参考にしましょう。
またpyjwtの使い方については以下ページを参考にします。
IVS公式ドキュメントから、JWTのフィールドにはheader
、payload
そしてsignature
の3項目があることがわかります。まずheader
についてはtoke typeがJWT
であることを押さえておきつつ、署名アルゴリズムとしてES384
を使うことを確認しておきます。pyjwt使用の際にはalgorithm='ES384'
の指定だけで良さそうですね。
続いてpayload
部分です。ドキュメントに以下の例が記載されています。
{ "aws:channel-arn": <channel_arn>, "aws:access-control-allow-origin": "<your-website>", "exp": <unix timestamp> }
"aws:channel-arn"
については、IVS ChannelのARNですね、マネジメントコンソールなどから、arn:aws:ivs:us-west-2:123456789012:channel/XXXXXXXXXXXX
な形式のARNを確認しておきましょう。
"aws:access-control-allow-origin"
はクロスオリジンリクエストを許可するかどうか、CORS設定を行うことができます。今回はAmazon IVS Player含めたhtmlファイルをS3でホスティグした環境で再生確認をします。S3バケット名をmy-s3-bucket
、リージョンを東京リージョンとして、"https://my-s3-bucket.s3-ap-northeast-1.amazonaws.com"
を指定することにします。指定はドメイン名のみではなく、プロトコル部分https://
も必要ですので注意しましょう。(私ははじめ、指定し忘れてハマりかけました。)
最後のexp
はtokenの有効期限になります。Unixタイプスタンプで指定します。
以上3つの項目をpayload
としてJWT生成時、pyjwtではjwt.encode
呼び出し時に指定します。またこの際に秘密鍵についても必要となります。この秘密鍵についてはPython環境から読み込める場所に配置しておきます。(今回であればAmazon Linux 2が稼働しているEC2インスタンス上にコピーしておきました。)
Python対話型インタプリタ上でのJWT作成
では実際にJWTを作成していきます。Python対話型インタプリタを起動し、datetime
とjwt
をimport
します。
[ec2-user@ip-10-82-21-161 ~]$ python3 Python 3.7.9 (default, Aug 27 2020, 21:59:41) [GCC 7.3.1 20180712 (Red Hat 7.3.1-9)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import datetime >>> import jwt >>>
tokenの有効期限は3日後とします。以下で最終的にUnixタイムスタンプ形式に変換できることを確認しておきます。
>>> datetime.datetime.utcnow() + 3*(datetime.timedelta(days=1)) datetime.datetime(2020, 9, 30, 8, 26, 10, 171448) >>> expires = datetime.datetime.utcnow() + 3*(datetime.timedelta(days=1)) >>> expires datetime.datetime(2020, 9, 30, 8, 26, 51, 851024) >>> int(expires.timestamp()) 1601454411 >>>
これを用いてpayloadを指定します。
>>> payload = { ... "aws:channel-arn": "arn:aws:ivs:us-west-2:123456789012:channel/XXXXXXXXXXXX", ... "aws:access-control-allow-origin": "https://my-s3-bucket.s3-ap-northeast-1.amazonaws.com", ... "exp": int(expires.timestamp()) ... } >>> payload {'aws:channel-arn': 'arn:aws:ivs:us-west-2:123456789012:channel/XXXXXXXXXXXX', 'aws:access-control-allow-origin': 'https://my-s3-bucket.s3-ap-northeast-1.amazonaws.com', 'exp': 1601454411} >>>
秘密鍵についても読み込んでおきます。
>>> secret = open('/home/ec2-user/ivs-playback-key-1.pem', 'r').read() >>> secret '-----BEGIN EC PRIVATE KEY-----\nXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n-----END EC PRIVATE KEY-----\n' >>>
そして、jwt.encodeを使ってtokenを生成します。
>>> encoded_jwt = jwt.encode(payload, secret, algorithm='ES384') >>> encoded_jwt b'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCJ9.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXImV4cCI6MTYwMTQ1NDQxMX0.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' >>>
この変数encoded_jwt
に格納されている情報、上記の表示で言えば`で囲まれた部分がtokenになります。では続いて、このtokenを使って実際にプライベートチャンネルの再生を行ってみます。
IVSのプライベートチャンネルでライブ配信して再生確認
tokenが作成できたので、これを使ってIVSのプライベートチャンネルのライブ視聴ができるか確認してみます。再生ページとして以下のhtmlを準備し、JWT生成次のpayloadでCORS設定をしたS3バケットでホスティングします。
<html> <head> <title>ivs-private-channel</title> <style> video { height: 100%; width: 100%; left: 0; top: 0; position: fixed; } </style> </head> <body> <video id="video-player" playsinline controls></video> <script src="https://player.live-video.net/1.0.0/amazon-ivs-player.min.js"></script> <script> var PLAYBACK_URL = "https://XXXXXXXXXXXX.us-west-2.playback.live-video.net/api/video/v1/us-west-2.123456789012.channel.XXXXXXXXXXXX.m3u8"; var token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCJ9.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXImV4cCI6MTYwMTQ1NDQxMX0.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; if (IVSPlayer.isPlayerSupported) { const player = IVSPlayer.create(); player.attachHTMLVideoElement(document.getElementById('video-player')); player.load(PLAYBACK_URL + "?token=" + token); player.play(); } </script> </body> </html>
Streaming SoftwareにIVSチャンネルの設定を行い、映像を打ち上げてライブ配信を開始します。今回Streaming SoftwareはiPhone 6s上でZixi ONAIRを使用しました。
マネジメントコンソールでライブ配信が開始されている状態であることを確認します。プライベートチャンネルなのでプレビューは参照できません。
再生ページを確認してみます。問題なくライブ配信が視聴できますね!
まとめ
Amazon Interactive Video Service(Amazon IVS)のプライベートチャンネルで再生に必要なtokenをPythonの対話型インタプリタ環境で作成してみました。JWTの作成方法自体はAWS環境固有のものではなく、一般的な方法が使えるかと思いますので、今回確認してみたPython以外でも様々な環境でJWTが作成できるかと思います。ただ冒頭でも述べましたが、実際にサービスに組み込み、ユーザ認証からのtoken作成などではサーバレス環境にしたり、そもそもプログラマブルに作成することが前提かと考えます。とはいえ、さくっと検証してみたい、なんてときにはこのように手元のPythonの対話型インタプリタで作成してしまう、なんてこともありなのではないでしょうか。